/*******************************************************************************
 * Copyright (c) 2015 Institute for Pervasive Computing, ETH Zurich and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.html.
 *
 * Contributors:
 *    Matthias Kovatsch - creator and main architect
 ******************************************************************************/
package hipad.coapservice;

import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.coap.CoAP.Type;
import org.eclipse.californium.core.server.resources.CoapExchange;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CHANGED;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.DELETED;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.NOT_ACCEPTABLE;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.NOT_FOUND;
import static org.eclipse.californium.core.coap.MediaTypeRegistry.TEXT_PLAIN;

/**
 * This resource implements a test of specification for the
 * ETSI IoT CoAP Plugtests, London, UK, 7--9 Mar 2014.
 */
public class ZHTimeObserve extends CoapResource {

    // Members ////////////////////////////////////////////////////////////////

    private byte[] data = null;
    private int dataCf = TEXT_PLAIN;
    private boolean wasUpdated = false;

    // The current time represented as string
    private String time;

    /*
     * Constructor for a new TimeResource
     */
    public ZHTimeObserve(String name) {
        super(name);
        setObservable(true);
        getAttributes().setTitle("Observable resource which changes every 5 seconds");
        getAttributes().addResourceType("observe");
        getAttributes().setObservable();
        setObserveType(Type.CON);

        // Set timer task scheduling
        Timer timer = new Timer();
        timer.schedule(new TimeTask(), 0, 5000);
    }

    /*
     * Defines a new timer task to return the current time
     */
    private class TimeTask extends TimerTask {

        @Override
        public void run() {
            time = getTime();
            dataCf = TEXT_PLAIN;

            // Call changed to notify subscribers
            changed();
        }
    }

    /*
     * Returns the current time
     *
     * @return The current time
     */
    private String getTime() {
        DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        Date time = new Date();
        return dateFormat.format(time);
    }

    @Override
    public void handleGET(CoapExchange exchange) {

        exchange.setMaxAge(5);

        if (wasUpdated) {
            exchange.respond(CONTENT, data, dataCf);
            wasUpdated = false;
        } else {
            exchange.respond(CONTENT, time, dataCf);
        }
    }

    @Override
    public void handlePUT(CoapExchange exchange) {

        if (!exchange.getRequestOptions().hasContentFormat()) {
            exchange.respond(BAD_REQUEST, "Content-Format not set");
            return;
        }

        // store payload
        storeData(exchange.getRequestPayload(), exchange.getRequestOptions().getContentFormat());

        // complete the request
        exchange.respond(CHANGED);
    }

    @Override
    public void handleDELETE(CoapExchange exchange) {
        wasUpdated = false;

        clearAndNotifyObserveRelations(NOT_FOUND);

        exchange.respond(DELETED);
    }


    // Internal ////////////////////////////////////////////////////////////////

    /*
     * Convenience function to store data contained in a
     * PUT/POST-Request. Notifies observing endpoints about
     * the change of its contents.
     */
    private synchronized void storeData(byte[] payload, int format) {

        wasUpdated = true;

        if (format != dataCf) {
            clearAndNotifyObserveRelations(NOT_ACCEPTABLE);
        }

        // set payload and content type
        data = payload;
        dataCf = format;

        getAttributes().clearContentType();
        getAttributes().addContentType(dataCf);

        // signal that resource state changed
        changed();
    }
}
